package com.lp.rpc.registry;

import com.lp.rpc.annotation.LpService;
import com.lp.rpc.constants.LpConstant;
import com.lp.rpc.constants.ZNodeType;
import com.lp.rpc.registry.watcher.NodeDataChangeWatcher;
import com.lp.rpc.server.LpServer;
import com.lp.rpc.utils.IpUti;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 服务注册
 *
 * @author steakLiu
 */
@AllArgsConstructor
public class ZkServiceRegistry implements ApplicationContextAware, InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(ZkServiceRegistry.class);
    /**
     * zk地址
     */
    private final String zkAddress;
    /**
     * 服务端口
     */
    private final int serverPort;


    /**
     *
     * 在程序启动的时候获取所有带有@LpService的注解，使用此注解表明将其申明为一个服务，
     * 然后将其接口名，组，版本进行组合，就为服务名，格式如（UserService:businessGroup:1.0.0）
     * 使用组是为了区分同意接口下的不同实现，版本为了区分接口的版本，防止多个同样的接口存在。
     * 将这些服务全部注册上zookeeper.
     *
     *
     * 根目录为lpRpc,子目录名为服务名，子目录附加数据为服务地址，端口，服务全路径，格式为（192.168.22.64:8080:com.lp.rpc.service.UserService）
     *
     */

    /**
     * @param applicationContext
     * @throws BeansException
     */
    @SneakyThrows
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> annotationMap = applicationContext.getBeansWithAnnotation(LpService.class);
        logger.info("服务注册中");
        logger.info("服务注册地址:{}",zkAddress);
        for (Object bean : annotationMap.values()){
            LpService annotation = bean.getClass().getAnnotation(LpService.class);
            Class<?>[] interfaces = bean.getClass().getInterfaces();
            for (Class<? extends Object> inter : interfaces){
                String interfaceName = inter.toString().replace("interface ","");

                /**
                 *
                 * 获取接口实现类的路径
                 */
                Class<?> superClass = Class.forName(interfaceName);
                Reflections reflections = new Reflections(interfaceName.replace(interfaceName.substring(
                        interfaceName.lastIndexOf(".") - 1
                ),""));
                Set<Class<?>> classes = reflections.getSubTypesOf((Class<Object>) superClass);
                String className = (classes.toArray(new Class[0]))[0].getName();


                /**
                 * 服务名拼接
                 */
                int i = interfaceName.lastIndexOf(".") + 1;
                String inters = interfaceName.substring(i);
                StringBuilder stringBuffer = new StringBuilder();
                stringBuffer.append(inters);
                if (!annotation.group().equals("")){
                    stringBuffer.append(":").append(annotation.group());
                }
                if (!annotation.version().equals("")){
                    stringBuffer.append(":").append(annotation.version());
                }
                String address = IpUti.getIp();
                String childService = LpConstant.ServiceRoot +"/"+stringBuffer.toString();

                /**
                 *
                 * 服务注册
                 */
                if (ZkOperation.exists(LpConstant.ServiceRoot,true) != null){
                    ZkOperation.create(childService,(address+":"+serverPort+":"+className).getBytes(), ZNodeType.EPHEMERAL);
                }
                /**
                 * 根节点不存在，代表第一次注册服务，需要创建一个持久化根节点
                 */
                else {
                    logger.info("持久化根节点：{} 不存在,正在创建",LpConstant.ServiceRoot);
                    ZkOperation.create(LpConstant.ServiceRoot, address.getBytes(), ZNodeType.PERSISTENT);
                }
            }
        }
    }

    @Override
    public void afterPropertiesSet(){
        /**
         *
         * 服务注册并启动netty服务端
         */
        List<String> list = ZkOperation.getChildren(LpConstant.ServiceRoot, new NodeDataChangeWatcher());
        logger.debug("服务列表：{}",list);
        LpServer lpServer = new LpServer();
        lpServer.startServer(serverPort);
    }
}
